package impl

import (
	"encoding/binary"
	"io"
	"net"
)

// IMPLEMENT INTERFACE: fight.NetProtocoler
type NetTCP struct {
}

func (*NetTCP) Listen(addr string) (net.Listener, error) {
	return net.Listen("tcp", addr)
}

func (*NetTCP) Accept(l net.Listener) (net.Conn, error) {
	return l.Accept()
}

// for client
func (*NetTCP) Dial(addr string) (net.Conn, error) {
	return net.Dial("tcp", addr)
}

// 包头长度
const LEN_BYTES = 4

func newHeader() []byte {
	return make([]byte, LEN_BYTES, LEN_BYTES)
}

func decodeHeader(header []byte) int {
	return int(binary.BigEndian.Uint32(header))
}

func encodeHeader(bsSize int, header []byte) {
	binary.BigEndian.PutUint32(header, uint32(bsSize))
}

// IMPLEMENT INTERFACE: fight.ReadWriter
type SimpleRW struct {
}

func (*SimpleRW) Read(r io.Reader) ([]byte, error) {
	header := newHeader()
	if _, err := io.ReadFull(r, header); err != nil {
		return nil, err
	}

	bsSize := decodeHeader(header)
	if bsSize == 0 {
		return []byte{}, nil
	}
	bs := make([]byte, bsSize, bsSize)
	if _, err := io.ReadFull(r, bs); err != nil {
		return nil, err
	}
	return bs, nil
}

func (*SimpleRW) Write(r io.Writer, bs []byte) (int, error) {
	bsSize := len(bs)
	buff := make([]byte, LEN_BYTES, LEN_BYTES+bsSize)
	encodeHeader(bsSize, buff)
	buff = append(buff, bs...)
	return r.Write(buff)
}

// IMPLEMENT INTERFACE: fight.ReadWriter
type BuffRW struct {
	rHeader []byte
	rB      []byte
	wB      []byte
}

func getBuff(needSize int, buff []byte) []byte {
	if needSize <= len(buff) {
		return buff
	}
	return make([]byte, needSize, needSize)
}

func NewBuffRW() *BuffRW {
	return &BuffRW{
		rHeader: newHeader(),
		rB:      make([]byte, 20, 20),
		wB:      make([]byte, 24, 24),
	}
}

func (rw *BuffRW) Read(r io.Reader) ([]byte, error) {
	if _, err := io.ReadFull(r, rw.rHeader); err != nil {
		return nil, err
	}
	bsSize := decodeHeader(rw.rHeader)
	bsBuff := getBuff(bsSize, rw.rB)
	if bsSize == 0 {
		return bsBuff[:0], nil
	}
	if _, err := io.ReadFull(r, bsBuff[:bsSize]); err != nil {
		return nil, err
	}
	return bsBuff[:bsSize], nil
}

func (rw *BuffRW) Write(r io.Writer, bs []byte) (int, error) {
	bsSize := len(bs)
	buffSize := LEN_BYTES + bsSize
	buff := getBuff(buffSize, rw.wB)
	encodeHeader(bsSize, buff)
	copy(buff[LEN_BYTES:], bs)
	n, err := r.Write(buff[:bsSize+LEN_BYTES])
	return n, err
}
